package org.ljy;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap; // 新增：引入TreeMap用于时间戳排序
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.io.FileUtils;

public class ZKLog385Extractor {
    // 时间戳正则（通用，匹配 yyyy-MM-dd HH:mm:ss,SSS）
    private static final Pattern TIMESTAMP_PATTERN = Pattern.compile("^(\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2},\\d{3})");

    // 选举通知日志：Notification: my state:XXX;（如LOOKING/FOLLOWING/LEADING）
    private static final Pattern NOTIFICATION_STATE_PATTERN = Pattern.compile("Notification: my state:([A-Z]+);");

    // 纯角色切换日志：Peer state changed: XXX（如following → 转为FOLLOWING）
    private static final Pattern PEER_SIMPLE_STATE_PATTERN = Pattern.compile("Peer state changed: ([a-z]+)");

    // 角色+阶段变更日志：Peer state changed: following - XXX（如following-discovery）
    private static final Pattern PEER_STAGE_STATE_PATTERN = Pattern.compile("Peer state changed: (following - [a-z]+)");

    // 显式角色日志：QuorumPeer@x] - XXX（x为任意数字，XXX为LOOKING/FOLLOWING/LEADING）
    private static final Pattern EXPLICIT_ROLE_PATTERN = Pattern.compile("QuorumPeer@\\d+\\] - (LOOKING|FOLLOWING|LEADING)");

    // 子文件夹名称正则（匹配 X_Xf 格式，X为数字）
    private static final Pattern SUB_DIR_PATTERN = Pattern.compile("\\d+_\\d+f");

    // 日志文件名称正则（匹配 zookeeper--server-NC1ZKX.out，X为1-5）
    private static final Pattern LOG_FILE_PATTERN = Pattern.compile("zookeeper--server-(NC1ZK[1-5])\\.out");


    /**
     * 提取指定主目录下所有符合条件的日志文件中的角色信息
     * @param mainPath 主目录路径
     * @return 整合结果，key为"子目录/文件名"，value为该文件的角色变更映射（时间戳→角色信息，已按时间排序）
     * @throws IOException  IO异常
     */
    public static Map<String, Map<String, String>> extractAllLogs(String mainPath) throws IOException {
        Map<String, Map<String, String>> allResults = new HashMap<>();
        File mainDir = new File(mainPath);

        if (!mainDir.exists() || !mainDir.isDirectory()) {
            throw new IllegalArgumentException("主目录不存在或不是目录：" + mainPath);
        }

        File[] subDirs = mainDir.listFiles(File::isDirectory);
        if (subDirs != null) {
            for (File subDir : subDirs) {
                if (SUB_DIR_PATTERN.matcher(subDir.getName()).matches()) {
                    processSubDirectory(subDir, allResults);
                }
            }
        }

        return allResults;
    }

    /**
     * 处理单个子目录，提取其中符合条件的日志文件信息
     */
    private static void processSubDirectory(File subDir, Map<String, Map<String, String>> allResults) throws IOException {
        File[] files = subDir.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isFile() && file.getName().endsWith(".out")) {
                    Matcher fileMatcher = LOG_FILE_PATTERN.matcher(file.getName());
                    if (fileMatcher.matches()) {
                        String serverId = fileMatcher.group(1);
                        String logFilePath = file.getAbsolutePath();

                        // 提取的角色信息已按时间戳排序（因为extractStateMap返回TreeMap）
                        Map<String, String> stateMap = extractStateMap(logFilePath, serverId);

                        String key = subDir.getName() + File.separator + file.getName();
                        allResults.put(key, stateMap);

                        System.out.printf("已处理文件：%s，提取到%d条角色变更记录（已按时间排序）%n", key, stateMap.size());
                    }
                }
            }
        }
    }

    /**
     * 提取单个日志文件中的角色变更信息（核心修改：使用TreeMap确保时间戳有序）
     * @return 角色变更映射（时间戳→角色信息，按时间戳从小到大排序）
     */
    public static Map<String, String> extractStateMap(String logFilePath, String serverId) throws IOException {
        // 关键修改：将HashMap改为TreeMap，自动按时间戳（键）排序
        Map<String, String> stateMap = new TreeMap<>();
        File logFile = new File(logFilePath);
        if (!logFile.exists()) {
            throw new IllegalArgumentException("日志文件不存在：" + logFilePath);
        }

        String lastState = null;
        for (String logLine : FileUtils.readLines(logFile, "UTF-8")) {
            String timestamp = extractTimestamp(logLine);
            String state = null;

            if (logLine.contains("Notification: my state:")) {
                state = matchPattern(logLine, NOTIFICATION_STATE_PATTERN);
            } else if (logLine.contains("Peer state changed: following - ")) {
                state = matchPattern(logLine, PEER_STAGE_STATE_PATTERN);
            } else if (logLine.contains("Peer state changed: ")) {
                state = matchPattern(logLine, PEER_SIMPLE_STATE_PATTERN);
                if (state != null) {
                    state = state.toUpperCase();
                }
            } else if (logLine.contains("QuorumPeer@") && logLine.contains("] - ")) {
                state = matchPattern(logLine, EXPLICIT_ROLE_PATTERN);
            }

            // 去重逻辑不变，TreeMap会自动按时间戳排序
            if (timestamp != null && state != null) {
                if (lastState == null || !state.equals(lastState)) {
                    stateMap.put(timestamp, serverId + ":" + state);
                    lastState = state;
                }
            }
        }
        return stateMap;
    }

    /**
     * 提取日志行的时间戳
     */
    private static String extractTimestamp(String logLine) {
        Matcher matcher = TIMESTAMP_PATTERN.matcher(logLine);
        return matcher.find() ? matcher.group(1) : null;
    }

    /**
     * 通用正则匹配方法
     */
    private static String matchPattern(String logLine, Pattern pattern) {
        Matcher matcher = pattern.matcher(logLine);
        return matcher.find() ? matcher.group(1) : null;
    }

    /**
     * 测试方法（输出结果已按时间戳排序）
     */
    public static void main(String[] args) {
        String mainPath = "/home/losewinner/crashfuzz/zk-3.8.5-c1-newCluster/crashfuzz-outputs/monitor_backup";
        try {
            Map<String, Map<String, String>> allResults = extractAllLogs(mainPath);

            System.out.println("\n===== 所有日志文件角色变更汇总（按时间排序） =====");
            allResults.forEach((fileKey, stateMap) -> {
                System.out.println("\n文件：" + fileKey);
                // TreeMap的forEach遍历顺序就是时间戳从小到大的顺序
                stateMap.forEach((timestamp, state) -> {
                    System.out.printf("  %s -> %s%n", timestamp, state);
                });
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}